﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;

namespace NMbsDuplex.ProtocalBuffer.Bifrost
{
    using NMbsDuplex.TCPServerX.Deamon;
    /* 
        * 1.文件名长度 block[1(0~255)] 
        * 最终的 9字节首部为 : 
        *               TCP 首包
        *               [FLen][FC1][FC2][FC3][FC4][C32.1][C32.2][C32.3][C32.4] | 固定为9个字节
        * 
        *  TCP 后续包 Begin
        *               [FName1]...[FNameN]                      | dynamic动态文件名
        * [FCbyte1].[FCbyte1].[FCbyte1].[FCbyte1].[FCbyte1]      | dynamic动态内容
        * [FCbyte1].[FCbyte1].[FCbyte1].[FCbyte1].[FCbyte1]      | dynamic动态内容
        * [FCbyte1]...............................[FCbyteN]      | dynamic动态内容
        * TCP 后续包 End...
        * 
        * 4.文件名转码字节流byte[M] + 整个文件流bytes[N] = 第4个区块 
    */
    public static partial class HolySword
    {
        public static IEnumerable<byte> TCPPacking(string fileUrl)
        {
            var memoryCache = new List<byte>();
            using (var sr = new FileStream(fileUrl, FileMode.Open))
            {
                // 1.抓取传入的URL 解析成文件流 1.统计首部[0] 2.写入block[3]
                string realName = Path.GetFileName(sr.Name);
                byte[] dynamicName =  TCPExtension.blockCoder.GetBytes(realName);
                memoryCache.Add(Convert.ToByte(dynamicName.Length));
                // 2.压入首部[1-4] 数据区长度值转换 [255][255][129][0]
                byte[] h4 = BitConverter.GetBytes(sr.Length);
                for (int i = 0; i < 4; i++) memoryCache.Add(h4[i]);
                // 3.模拟生成假的CRC32循环校检值 - 生成CRC32值写入首部[5-9]
                memoryCache.Add(Convert.ToByte('C'));
                memoryCache.Add(Convert.ToByte('R'));
                memoryCache.Add(Convert.ToByte('C'));
                memoryCache.Add(Convert.ToByte("32"));
                // 4.对拷文件名转码
                for (int i = 0; i < dynamicName.Length; i++)
                    memoryCache.Add(dynamicName[i]);
                // 5.逐字逐句地读取每一个字节到内存数据结构
                int code = 0;
                while ((code = sr.ReadByte()) != -1)
                    memoryCache.Add(Convert.ToByte(code));
            }
            return memoryCache;
        }
        /// <summary>
        /// TCP解包函数 解析首部 [文件名长度.1] [内容长度.4] 3.[CRC32.4] 
        /// </summary>
        /// <param name="Listener">文件描述符</param>
        /// <returns> 返回 1.页码 2.首字节长度,用于抽取出块24中的文件名 </returns>
        public static Tuple<int, int> TCPUnPacking1st(this Socket Listener)
        {
            // 输出首部9个字节
            byte[] x6 = new byte[9];
            int N = Listener.Receive(x6);
            int FLen = Convert.ToInt32(x6[0]);
            byte[] b4 = new byte[4];
            for (int i = 1; i < 5; i++)
                b4[i - 1] = x6[i];

            int CodeLen = BitConverter.ToInt32(b4, 0);
            return new Tuple<int, int>(FLen,CodeLen);
        }
        /// <summary>
        /// TCP后续核心函数 - 传输后续文件流
        /// </summary>
        /// <param name="Listener"> 文件描述符 </param>
        /// <param name="pageSize"> 循环接收的次数 - 在首部9字节的函数确定 后续传输的for循环次数PageSize </param>
        /// <param name="FLen"> 接收整包中有多少字节是文件名长度 </param>
        public static void TCPUnPackingContinuously(
            this Socket Listener,int FLen,int CodeLen)
        {
            /* 计算TCP分片儿传输的总次数 */
            int SUMMD5 = CodeLen + FLen; // 统计传输总字节数(流量)

            #region 困扰我很久的TCP丢包问题解决方案 ： 必须用While循环替换For计数循环
            var LHN = new List<byte>(0);
            int N83 = 0 , times = 0;
            while(N83 < SUMMD5)
            {
                byte[] content = new byte[TCPExtension.PACKSIZE];
                int N38 = Listener.Receive(content);
                if (N38 > 0)
                {
                    for (int index = 0; index < N38; index++)
                    {
                        LHN.Add(content[index]);
                    }
                    N83 += N38;
                    times++;
                    Console.WriteLine("接受 TCP分片儿大小[{1}:{0}kb]", N38, times);
                }else/* 但传输完成则跳出循环 */
                {
                    break;
                }
            }
            string FName = Encoding.UTF8.GetString(LHN.ToArray(),0,FLen);
            LHN.RemoveRange(0, FLen);
            WashMemory(LHN, FName);
            #endregion

            /* 传输结束 进行必要的Log输出 */
            Console.WriteLine(" *** \n\n\t ~~~~~~~~~ 理论接收长度`{0}` ~~~~~~~~~ ", CodeLen);
            Console.WriteLine(" *** \t ~~~~~~~~~ 实际接收长度`{0}` ~~~~~~~~~ \n\n", N83);
            Console.WriteLine(" *** \t 文件名长度`{0}` \n *** 传输总次数[{1}]\n",FLen,times);
        }
        internal static void WashMemory(IEnumerable<byte> resultCode, string fileUrl)
        {
            List<byte> CODES = resultCode as List<byte>;
            string FPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            FPath += Path.DirectorySeparatorChar + "诚昊传输双工";
            FPath += Path.DirectorySeparatorChar + DateTime.Now.ToString("D");
            // 创建专用传输路径
            if (!Directory.Exists(FPath))
            {
                Directory.CreateDirectory(FPath);
            }
            FPath += "/" + fileUrl;
            using (var fr = new FileStream(FPath, FileMode.OpenOrCreate))
            {
                fr.Seek(0, SeekOrigin.Begin);
                for (int i = 0; i < CODES.Count; i++)
                    fr.WriteByte(CODES[i]);
#if DEBUG
                for (int k = 0; k < 1024; k++)
                    if (CODES[k] != 0x00)
                        Console.Write("[{0:X2}].", CODES[k]);
                Console.WriteLine("\n传输完成 Done(OK~)");
                Console.WriteLine("成功写入文件流 ---> " + fr.Name);
#endif
            }
        }
    }
}
